<!doctype html>




<html class="theme-next pisces" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>



<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />












  
  
    
  
  <link href="//cdn.staticfile.org/fancybox/2.1.5/jquery.fancybox.min.css" rel="stylesheet" type="text/css" />




  
  
  
  

  
    
    
  

  
    
      
    

    
  

  

  
    
      
    

    
  

  
    
      
    

    
  

  
    
    
    <link href="//fonts.lug.ustc.edu.cn/css?family=Monda:300,300italic,400,400italic,700,700italic|Roboto Slab:300,300italic,400,400italic,700,700italic|Lobster Two:300,300italic,400,400italic,700,700italic|PT Mono:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






  

<link href="//cdn.staticfile.org/font-awesome/4.6.2/css/font-awesome.min.css" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=5.1.0" rel="stylesheet" type="text/css" />


  <meta name="keywords" content="Web,JavaScript," />








  <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=5.1.0" />






<meta name="description" content="高级技巧高级函数函数柯里化与函数绑定紧密相关的主题是函数柯里化（function currying），它用于创建已经设置好了一个或多个参数的函数。基本方法和函数绑定是一样的：使用一个闭包返回一个函数。两者的区别在于，当函数被调用时，返回的函数还需要设置一些传入的参数。
12345678function add(num1, num2)&amp;#123;    return num1 + num2;&amp;#12">
<meta property="og:type" content="article">
<meta property="og:title" content="《 JavaScript 高级程序设计》备忘-第二十二章-2">
<meta property="og:url" content="http://codingme.xyz/2016/11/07/javascript-note-22-2/index.html">
<meta property="og:site_name" content="CodingMe">
<meta property="og:description" content="高级技巧高级函数函数柯里化与函数绑定紧密相关的主题是函数柯里化（function currying），它用于创建已经设置好了一个或多个参数的函数。基本方法和函数绑定是一样的：使用一个闭包返回一个函数。两者的区别在于，当函数被调用时，返回的函数还需要设置一些传入的参数。
12345678function add(num1, num2)&amp;#123;    return num1 + num2;&amp;#12">
<meta property="og:updated_time" content="2016-11-09T16:36:43.066Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="《 JavaScript 高级程序设计》备忘-第二十二章-2">
<meta name="twitter:description" content="高级技巧高级函数函数柯里化与函数绑定紧密相关的主题是函数柯里化（function currying），它用于创建已经设置好了一个或多个参数的函数。基本方法和函数绑定是一样的：使用一个闭包返回一个函数。两者的区别在于，当函数被调用时，返回的函数还需要设置一些传入的参数。
12345678function add(num1, num2)&amp;#123;    return num1 + num2;&amp;#12">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Pisces',
    sidebar: {"position":"right","display":"post"},
    fancybox: true,
    motion: false,
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://codingme.xyz/2016/11/07/javascript-note-22-2/"/>





  <title> 《 JavaScript 高级程序设计》备忘-第二十二章-2 | CodingMe </title>
</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  










  
  
    
  

  <div class="container one-collumn sidebar-position-right page-post-detail ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-meta ">
  

  <div class="custom-logo-site-title">
    <a href="/"  class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <span class="site-title">CodingMe</span>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>
  <p class="site-subtitle"></p>
</div>

<div class="site-nav-toggle">
  <button>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
  </button>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/about" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />
            
            关于
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/tags" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            分类
          </a>
        </li>
      

      
        <li class="menu-item menu-item-search">
          
            <a href="javascript:;" class="popup-trigger">
          
            
              <i class="menu-item-icon fa fa-search fa-fw"></i> <br />
            
            搜索
          </a>
        </li>
      
    </ul>
  

  
    <div class="site-search">
      
  <div class="popup">
 <span class="search-icon fa fa-search"></span>
 <input type="text" id="local-search-input">
 <div id="local-search-result"></div>
 <span class="popup-btn-close">close</span>
</div>


    </div>
  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
  <link itemprop="mainEntityOfPage" href="http://codingme.xyz/2016/11/07/javascript-note-22-2/">

  <span style="display:none" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <meta itemprop="name" content="Huang SJ">
    <meta itemprop="description" content="">
    <meta itemprop="image" content="/uploads/avatar.jpg">
  </span>

  <span style="display:none" itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
    <meta itemprop="name" content="CodingMe">
    <span style="display:none" itemprop="logo" itemscope itemtype="http://schema.org/ImageObject">
      <img style="display:none;" itemprop="url image" alt="CodingMe" src="">
    </span>
  </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
            
            
              
                《 JavaScript 高级程序设计》备忘-第二十二章-2
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">
            

            

            
          </span>

          

          
            
          

          

          
          

          

          

        </div>
      </header>
    


    <div class="post-body" itemprop="articleBody">

      
      

      
        <h1 id="高级技巧"><a href="#高级技巧" class="headerlink" title="高级技巧"></a>高级技巧</h1><h2 id="高级函数"><a href="#高级函数" class="headerlink" title="高级函数"></a>高级函数</h2><h3 id="函数柯里化"><a href="#函数柯里化" class="headerlink" title="函数柯里化"></a>函数柯里化</h3><p>与函数绑定紧密相关的主题是函数柯里化（function currying），它用于创建已经设置好了一个或多个参数的函数。<br>基本方法和函数绑定是一样的：使用一个闭包返回一个函数。两者的区别在于，当函数被调用时，返回的函数还需要设置一些传入的参数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params">num1, num2</span>)</span>&#123;</div><div class="line">    <span class="keyword">return</span> num1 + num2;</div><div class="line">&#125;</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">curriedAdd</span>(<span class="params">num2</span>)</span>&#123;</div><div class="line">    <span class="keyword">return</span> add(<span class="number">5</span>, num2);</div><div class="line">&#125;</div><div class="line">alert(add(<span class="number">2</span>, <span class="number">3</span>)); <span class="comment">//5</span></div><div class="line">alert(curriedAdd(<span class="number">3</span>)); <span class="comment">//8</span></div></pre></td></tr></table></figure>
<p>后者本质上是在任何情况下第一个参数为5的add()版本。<br>尽管从技术上来说curriedAdd()并非柯里化的函数，但它很好地展示了其概念。<br><a id="more"></a><br>柯里化函数通常由以下步骤动态创建：调用另一个函数并为它传入要柯里化的函数和必要参数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">curry</span>(<span class="params">fn</span>) </span>&#123;</div><div class="line">    <span class="keyword">var</span> args = <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>, <span class="number">1</span>);</div><div class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">        <span class="keyword">var</span> innerArgs = <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>);</div><div class="line">        <span class="keyword">var</span> finalArgs = args.concat(innerArgs);</div><div class="line">        <span class="keyword">return</span> fn.apply(<span class="literal">null</span>, finalArgs);</div><div class="line">    &#125;;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>curry()函数的主要工作就是将被返回函数的参数进行排序。<br>curry()的第一个参数是要进行柯里化的函数，其他参数是要传入的值。<br>为了获取第一个参数之后的所有参数，在arguments 对象上调用了slice()方法，并传入参数1 表示被返回的数组包含从第二个参数开始的所有参数。<br>然后args 数组包含了来自外部函数的参数。<br>在内部函数中，创建了innerArgs 数组用来存放所有传入的参数（又一次用到了slice()）。<br>有了存放来自外部函数和内部函数的参数数组后，就可以使用concat()方法将它们组合为finalArgs，然后使用apply()将结果传递给该函数。<br>注意这个函数并没有考虑到执行环境，所以调用apply()时第一个参数是null。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params">num1, num2</span>)</span>&#123;</div><div class="line">    <span class="keyword">return</span> num1 + num2;</div><div class="line">&#125;</div><div class="line"><span class="keyword">var</span> curriedAdd = curry(add, <span class="number">5</span>);</div><div class="line">alert(curriedAdd(<span class="number">3</span>)); <span class="comment">//8</span></div></pre></td></tr></table></figure>
<p>创建了第一个参数绑定为5 的add()的柯里化版本。当调用curriedAdd()并传入3 时，3 会成为add()的第二个参数，同时第一个参数依然是5，最后结果便是和8。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params">num1, num2</span>)</span>&#123;</div><div class="line">    <span class="keyword">return</span> num1 + num2;</div><div class="line">&#125;</div><div class="line"><span class="keyword">var</span> curriedAdd = curry(add, <span class="number">5</span>, <span class="number">12</span>);</div><div class="line">alert(curriedAdd()); <span class="comment">//17</span></div></pre></td></tr></table></figure>
<p>在这里，柯里化的add()函数两个参数都提供了，所以以后就无需再传递它们了。<br>函数柯里化还常常作为函数绑定的一部分包含在其中，构造出更为复杂的bind()函数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">bind</span>(<span class="params">fn, context</span>) </span>&#123;</div><div class="line">    <span class="keyword">var</span> args = <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>, <span class="number">2</span>);</div><div class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">        <span class="keyword">var</span> innerArgs = <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>);</div><div class="line">        <span class="keyword">var</span> finalArgs = args.concat(innerArgs);</div><div class="line">        <span class="keyword">return</span> fn.apply(context, finalArgs);</div><div class="line">    &#125;;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>对curry()函数的主要更改在于传入的参数个数，以及它如何影响代码的结果。<br>curry()仅仅接受一个要包裹的函数作为参数，而bind()同时接受函数和一个object 对象。<br>这表示给被绑定的函数的参数是从第三个开始而不是第二个，这就要更改slice()的第一处调用。<br>另一处更改是在倒数第3 行将object 对象传给apply()。<br>当使用bind()时，它会返回绑定到给定环境的函数，并且可能它其中某些函数参数已经被设好。<br>当你想除了event 对象再额外给事件处理程序传递参数时，这非常有用。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> handler = &#123;</div><div class="line">    message: <span class="string">"Event handled"</span>,</div><div class="line">    handleClick: <span class="function"><span class="keyword">function</span>(<span class="params">name, event</span>) </span>&#123;</div><div class="line">        alert(<span class="keyword">this</span>.message + <span class="string">":"</span> + name + <span class="string">":"</span> + event.type);</div><div class="line">    &#125;</div><div class="line">&#125;;</div><div class="line"><span class="keyword">var</span> btn = <span class="built_in">document</span>.getElementById(<span class="string">"my-btn"</span>);</div><div class="line">EventUtil.addHandler(btn, <span class="string">"click"</span>, bind(handler.handleClick, handler, <span class="string">"my-btn"</span>));</div></pre></td></tr></table></figure>
<p>handler.handleClick()方法接受了两个参数：要处理的元素的名字和event 对象。<br>作为第三个参数传递给bind()函数的名字，又被传递给了handler.handleClick()，而handler.handleClick()也会同时接收到event 对象。</p>
<p>ECMAScript 5 的bind()方法也实现函数柯里化，只要在this 的值之后再传入另一个参数即可。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> handler = &#123;</div><div class="line">    message: <span class="string">"Event handled"</span>,</div><div class="line">    handleClick: <span class="function"><span class="keyword">function</span>(<span class="params">name, event</span>) </span>&#123;</div><div class="line">        alert(<span class="keyword">this</span>.message + <span class="string">":"</span> + name + <span class="string">":"</span> + event.type);</div><div class="line">    &#125;</div><div class="line">&#125;;</div><div class="line"><span class="keyword">var</span> btn = <span class="built_in">document</span>.getElementById(<span class="string">"my-btn"</span>);</div><div class="line">EventUtil.addHandler(btn, <span class="string">"click"</span>, handler.handleClick.bind(handler, <span class="string">"my-btn"</span>));</div></pre></td></tr></table></figure>
<p>JavaScript 中的柯里化函数和绑定函数提供了强大的动态函数创建功能。<br>使用bind()还是curry()要根据是否需要object 对象响应来决定。它们都能用于创建复杂的算法和功能，当然两者都不应滥用，因为每个函数都会带来额外的开销。</p>
<h2 id="防篡改对象"><a href="#防篡改对象" class="headerlink" title="防篡改对象"></a>防篡改对象</h2><p>ECMAScript 5 致力于解决这个问题，可以让开发人员定义防篡改对象（tamper-proof object）。</p>
<h3 id="不可扩展对象"><a href="#不可扩展对象" class="headerlink" title="不可扩展对象"></a>不可扩展对象</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> person = &#123; name: <span class="string">"Nicholas"</span> &#125;;</div><div class="line"><span class="built_in">Object</span>.preventExtensions(person);</div></pre></td></tr></table></figure>
<p>在调用了Object.preventExtensions()方法后，就不能给person 对象添加新属性和方法了。<br>在非严格模式下，给对象添加新成员会导致静默失败，因此person.age 将是undefined。<br>而在严格模式下，尝试给不可扩展的对象添加新成员会导致抛出错误。<br>但已有的成员则丝毫不受影响。你仍然还可以修改和删除已有的成员。<br>使用Object.istExtensible()方法还可以确定对象是否可以扩展。</p>
<h3 id="密封的对象"><a href="#密封的对象" class="headerlink" title="密封的对象"></a>密封的对象</h3><p>第二个保护级别是密封对象（sealed object）。<br>密封对象不可扩展，不能删除属性和方法，因为不能使用Object.defineProperty()把数据属性修改为访问器属性，或者相反。<br>属性值是可以修改的。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> person = &#123; name: <span class="string">"Nicholas"</span> &#125;;</div><div class="line"><span class="built_in">Object</span>.seal(person);</div></pre></td></tr></table></figure>
<p>在严格模式下，尝试添加或删除对象成员都会导致抛出错误。<br>使用Object.isSealed()方法可以确定对象是否被密封了。<br>因为被密封的对象不可扩展，所以用Object.isExtensible()检测密封的对象也会返回false。</p>
<h3 id="冻结的对象"><a href="#冻结的对象" class="headerlink" title="冻结的对象"></a>冻结的对象</h3><p>最严格的防篡改级别是冻结对象（frozen object）。<br>冻结的对象既不可扩展，又是密封的，而且对象数据属性的[[Writable]]特性会被设置为false。<br>如果定义[[Set]]函数，访问器属性仍然是可写的。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> person = &#123; name: <span class="string">"Nicholas"</span> &#125;;</div><div class="line"><span class="built_in">Object</span>.freeze(person);</div></pre></td></tr></table></figure>
<p>与密封和不允许扩展一样，对冻结的对象执行非法操作在非严格模式下会被忽略，而在严格模式下会抛出错误。<br>当然，也有一个Object.isFrozen()方法用于检测冻结对象。<br>因为冻结对象既是密封的又是不可扩展的，所以用Object.isExtensible()和Object.isSealed()检测冻结对象将分别返回false和true。</p>
<p>对JavaScript 库的作者而言，冻结对象是很有用的。<br>因为JavaScript 库最怕有人意外（或有意）地修改了库中的核心对象。冻结（或密封）主要的库对象能够防止这些问题的发生。</p>
<h2 id="高级定时器"><a href="#高级定时器" class="headerlink" title="高级定时器"></a>高级定时器</h2><p>使用setTimeout()和setInterval()创建的定时器可以用于实现有趣且有用的功能。<br>JavaScript 是运行于单线程的环境中的，而定时器仅仅只是计划代码在未来的某个时间执行。<br>执行时机是不能保证的，因为在页面的生命周期中，不同时间可能有其他代码在控制JavaScript 进程。<br>在页面下载完后的代码运行、事件处理程序、Ajax 回调函数都必须使用同样的线程来执行。<br>实际上，浏览器负责进行排序，指派某段代码在某个时间点运行的优先级。</p>
<p>可以把JavaScript 想象成在时间线上运行的。<br>当页面载入时，首先执行是任何包含在<code>&lt;script&gt;</code>元素中的代码，通常是页面生命周期后面要用到的一些简单的函数和变量的声明，不过有时候也包含一些初始数据的处理。<br>在这之后，JavaScript 进程将等待更多代码执行。<br>当进程空闲的时候，下一个代码会被触发并立刻执行。<br>例如，当点击某个按钮时，onclick 事件处理程序会立刻执行，只要JavaScript 进程处于空闲状态。</p>
<p>除了主JavaScript 执行进程外，还有一个需要在进程下一次空闲时执行的代码队列。<br>随着页面在其生命周期中的推移，代码会按照执行顺序添加入队列。<br>例如，当某个按钮被按下时，它的事件处理程序代码就会被添加到队列中，并在下一个可能的时间里执行。<br>当接收到某个Ajax 响应时，回调函数的代码会被添加到队列。<br><strong>在JavaScript 中没有任何代码是立刻执行的，但一旦进程空闲则尽快执行。</strong><br>定时器对队列的工作方式是，当特定时间过去后将代码插入。<br><strong>给队列添加代码并不意味着对它立刻执行，而只能表示它会尽快执行。</strong><br>设定一个150ms 后执行的定时器不代表到了150ms 代码就立刻执行，它表示代码会在150ms 后被加入到队列中。<br>如果在这个时间点上，队列中没有其他东西，那么这段代码就会被执行，表面上看上去好像代码就在精确指定的时间点上执行了。<br>其他情况下，代码可能明显地等待更长时间才执行。</p>
<p>关于定时器要记住的最重要的事情是，<strong>指定的时间间隔表示何时将定时器的代码添加到队列，而不是何时实际执行代码。</strong><br>队列中所有的代码都要等到JavaScript 进程空闲之后才能执行，而不管它们是如何添加到队列中的。</p>
<p>执行完一套代码后，JavaScript 进程返回一段很短的时间，这样页面上的其他处理就可以进行了。<br>由于JavaScript 进程会阻塞其他页面处理，所以必须有这些小间隔来防止用户界面被锁定（代码长时间运行中还有可能出现）。<br>这样设置一个定时器，可以确保在定时器代码执行前至少有一个进程间隔。</p>
<h3 id="重复的定时器"><a href="#重复的定时器" class="headerlink" title="重复的定时器"></a>重复的定时器</h3><p>使用setInterval()创建的定时器确保了定时器代码规则地插入队列中。<br>问题在于，定时器代码可能在代码再次被添加到队列之前还没有完成执行，结果导致定时器代码连续运行好几次，而之间没有任何停顿。<br>幸好，JavaScript 引擎够聪明，能避免这个问题。<br>当使用setInterval()时，仅当没有该定时器的任何其他代码实例时，才将定时器代码添加到队列中。<br>这确保了定时器代码加入到队列中的最小时间间隔为指定间隔。<br>这种重复定时器的规则有两个问题：<br>(1) 某些间隔会被跳过；(2) 多个定时器的代码执行之间的间隔可能会比预期的小。</p>
<p>假设，某个onclick 事件处理程序使用setInterval()设置了一个200ms 间隔的重复定时器。<br>如果事件处理程序花了300ms 多一点的时间完成，同时定时器代码也花了差不多的时间，就会同时出现跳过间隔且连续运行定时器代码的情况。</p>
<p>为了避免setInterval()的重复定时器的这2个缺点，你可以用如下模式使用链式setTimeout()调用。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</div><div class="line">    <span class="comment">//处理中</span></div><div class="line">    setTimeout(<span class="built_in">arguments</span>.callee, interval);</div><div class="line">&#125;, interval);</div></pre></td></tr></table></figure>
<p>这个模式链式调用了setTimeout()，每次函数执行的时候都会创建一个新的定时器。<br>第二个setTimeout()调用使用了arguments.callee 来获取对当前执行的函数的引用，并为其设置另外一个定时器。<br><strong>这样做的好处是，在前一个定时器代码执行完之前，不会向队列插入新的定时器代码，确保不会有任何缺失的间隔。</strong><br>而且，它可以保证在下一次定时器代码执行之前，至少要等待指定的间隔，避免了连续的运行。<br>这个模式主要用于重复定时器:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</div><div class="line"><span class="keyword">var</span> div = <span class="built_in">document</span>.getElementById(<span class="string">"myDiv"</span>);</div><div class="line">left = <span class="built_in">parseInt</span>(div.style.left) + <span class="number">5</span>;</div><div class="line">div.style.left = left + <span class="string">"px"</span>;</div><div class="line"><span class="keyword">if</span> (left &lt; <span class="number">200</span>)&#123;</div><div class="line">    setTimeout(<span class="built_in">arguments</span>.callee, <span class="number">50</span>);</div><div class="line">&#125;</div><div class="line">&#125;, <span class="number">50</span>);</div></pre></td></tr></table></figure>
<p>这段定时器代码每次执行的时候将一个<code>&lt;div&gt;</code>元素向右移动，当左坐标在200 像素的时候停止。JavaScript 动画中使用这个模式很常见。</p>
<p>每个浏览器窗口、标签页、或者frame 都有其各自的代码执行队列。<br>这意味着，进行跨frame 或者跨窗口的定时调用，当代码同时执行的时候可能会导致竞争条件。<br>无论何时需要使用这种通信类型，最好是在接收frame 或者窗口中创建一个定时器来执行代码。</p>
<h3 id="Yielding-Processes"><a href="#Yielding-Processes" class="headerlink" title="Yielding Processes"></a>Yielding Processes</h3><p>运行在浏览器中的JavaScript 都被分配了一个确定数量的资源。<br>不同于桌面应用往往能够随意控制他们要的内存大小和处理器时间，JavaScript 被严格限制了，以防止恶意的Web 程序员把用户的计算机搞挂了。<br>其中一个限制是长时间运行脚本的制约，如果代码运行超过特定的时间或者特定语句数量就不让它继续执行。<br>如果代码达到了这个限制，会弹出一个浏览器错误的对话框，告诉用户某个脚本会用过长的时间执行，询问是允许其继续执行还是停止它。<br>所有JavaScript 开发人员的目标就是，确保用户永远不会在浏览器中看到这个令人费解的对话框。定时器是绕开此限制的方法之一。</p>
<p>脚本长时间运行的问题通常是由两个原因之一造成的：过长的、过深嵌套的函数调用或者是进行大量处理的循环。<br>这两者中，后者是较为容易解决的问题。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i=<span class="number">0</span>, len=data.length; i &lt; len; i++)&#123;</div><div class="line">process(data[i]);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这个模式的问题在于要处理的项目的数量在运行前是不可知的。<br>如果完成process()要花100ms，只有2 个项目的数组可能不会造成影响，但是10 个的数组可能会导致脚本要运行一秒钟才能完成。<br>数组中的项目数量直接关系到执行完该循环的时间长度。<br>同时由于JavaScript 的执行是一个阻塞操作，脚本运行所花时间越久，用户无法与页面交互的时间也越久。</p>
<p>在展开该循环之前，需要回答以下两个重要的问题。</p>
<p>1.该处理是否必须同步完成？如果这个数据的处理会造成其他运行的阻塞，那么最好不要改动它。<br>不过，如果对这个问题的回答确定为“否”，那么将某些处理推迟到以后是个不错的备选项。<br>2.数据是否必须按顺序完成？通常，数组只是对项目的组合和迭代的一种简便的方法而无所谓顺序。<br>如果项目的顺序不是非常重要，那么可能可以将某些处理推迟到以后。</p>
<p>如果某个循环占用了大量时间，同时对于上述两个问的回答都是“否”，那么就可以使用定时器分割这个循环。<br>这是一种叫做数组分块（array chunking）的技术，小块小块地处理数组，通常每次一小块。<br>基本的思路是为要处理的项目创建一个队列，然后使用定时器取出下一个要处理的项目进行处理，接着再设置另一个定时器。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</div><div class="line"><span class="comment">//取出下一个条目并处理</span></div><div class="line"><span class="keyword">var</span> item = array.shift();</div><div class="line">process(item);</div><div class="line"><span class="comment">//若还有条目，再设置另一个定时器</span></div><div class="line"><span class="keyword">if</span>(array.length &gt; <span class="number">0</span>)&#123;</div><div class="line">setTimeout(<span class="built_in">arguments</span>.callee, <span class="number">100</span>);</div><div class="line">&#125;</div><div class="line">&#125;, <span class="number">100</span>);</div></pre></td></tr></table></figure>
<p>在数组分块模式中，array 变量本质上就是一个“待办事宜”列表，它包含了要处理的项目。<br>使用shift()方法可以获取队列中下一个要处理的项目，然后将其传递给某个函数。<br>如果在队列中还有其他项目，则设置另一个定时器，并通过arguments.callee 调用同一个匿名函数。<br>如果想保持原数组不变，则应该传递数组的克隆，<code>array.concat()</code></p>
<p>数组分块的重要性在于它可以将多个项目的处理在执行队列上分开，在每个项目处理之后，给予其他的浏览器处理机会运行，这样就可能避免长时间运行脚本的错误。</p>
<p>一旦某个函数需要花50ms 以上的时间完成，那么最好看看能否将任务分割为一系列可以使用定时器的小任务。</p>
<h3 id="函数节流"><a href="#函数节流" class="headerlink" title="函数节流"></a>函数节流</h3><p>浏览器中某些计算和处理要比其他的昂贵很多。例如，DOM 操作比起非DOM 交互需要更多的内存和CPU 时间。<br>连续尝试进行过多的DOM 相关操作可能会导致浏览器挂起，有时候甚至会崩溃。<br>尤其在IE 中使用onresize 事件处理程序的时候容易发生，当调整浏览器大小的时候，该事件会连续触发。<br>在onresize 事件处理程序内部如果尝试进行DOM 操作，其高频率的更改可能会让浏览器崩溃。<br>为了绕开这个问题，你可以使用定时器对该函数进行节流。</p>
<p>函数节流背后的基本思想是指，某些代码不可以在没有间断的情况连续重复执行。<br>第一次调用函数，创建一个定时器，在指定的时间间隔之后运行代码。<br>当第二次调用该函数时，它会清除前一次的定时器并设置另一个。<br>如果前一个定时器已经执行过了，这个操作就没有任何意义。<br>然而，如果前一个定时器尚未执行，其实就是将其替换为一个新的定时器。<br>目的是只有在执行函数的请求停止了一段时间之后才执行。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> processor = &#123;</div><div class="line">timeoutId: <span class="literal">null</span>,</div><div class="line"><span class="comment">//实际进行处理的方法</span></div><div class="line">performProcessing: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</div><div class="line"><span class="comment">//实际执行的代码</span></div><div class="line">&#125;,</div><div class="line"><span class="comment">//初始处理调用的方法</span></div><div class="line">process: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</div><div class="line">clearTimeout(<span class="keyword">this</span>.timeoutId);</div><div class="line"><span class="keyword">var</span> that = <span class="keyword">this</span>;</div><div class="line"><span class="keyword">this</span>.timeoutId = setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</div><div class="line">that.performProcessing();</div><div class="line">&#125;, <span class="number">100</span>);</div><div class="line">&#125;</div><div class="line">&#125;;</div><div class="line"><span class="comment">//尝试开始执行</span></div><div class="line">processor.process();</div></pre></td></tr></table></figure>
<p>这段代码中，创建了一个叫做processor 对象。这个对象还有2 个方法：process()和performProcessing()。<br>前者是初始化任何处理所必须调用的，后者则实际进行应完成的处理。<br>当调用了process()，第一步是清除存好的timeoutId，来阻止之前的调用被执行。<br>然后，创建一个新的定时器调用performProcessing()。<br>由于setTimeout()中用到的函数的环境总是window，所以有必要保存this 的引用以方便以后使用。</p>
<p>时间间隔设为了100ms，这表示最后一次调用process()之后至少100ms 后才会调用performProcessing()。<br>所以如果100ms 之内调用了process()共20 次，performanceProcessing()仍只会被调用一次。<br>这个模式可以使用throttle()函数来简化，这个函数可以自动进行定时器的设置和清除:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">throttle</span>(<span class="params">method, context</span>) </span>&#123;</div><div class="line">clearTimeout(method.tId);</div><div class="line">method.tId= setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</div><div class="line">method.call(context);</div><div class="line">&#125;, <span class="number">100</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>throttle()函数接受两个参数：要执行的函数以及在哪个作用域中执行。<br>上面这个函数首先清除之前设置的任何定时器。<br>定时器ID 是存储在函数的tId 属性中的，第一次把方法传递给throttle()的时候，这个属性可能并不存在。<br>接下来，创建一个新的定时器，并将其ID 储存在方法的tId 属性中。<br>如果这是第一次对这个方法调用throttle()的话，那么这段代码会创建该属性。<br>定时器代码使用call()来确保方法在适当的环境中执行。如果没有给出第二个参数，那么就在全局作用域内执行该方法。</p>
<p>前面提到过，节流在resize 事件中是最常用的。如果基于该事件来改变页面布局的话，最好控制处理的频率，以确保浏览器不会在极短的时间内进行过多的计算。</p>
<p>假设有一个<code>&lt;div&gt;</code>元素需要保持它的高度始终等同于宽度:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">window</span>.onresize = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</div><div class="line"><span class="keyword">var</span> div = <span class="built_in">document</span>.getElementById(<span class="string">"myDiv"</span>);</div><div class="line">div.style.height = div. offsetWidth + <span class="string">"px"</span>;</div><div class="line">&#125;;</div></pre></td></tr></table></figure>
<p>这段非常简单的例子有两个问题可能会造成浏览器运行缓慢。<br>首先，要计算offsetWidth 属性，如果该元素或者页面上其他元素有非常复杂的CSS 样式，那么这个过程将会很复杂。<br>其次，设置某个元素的高度需要对页面进行回流来令改动生效。<br>如果页面有很多元素同时应用了相当数量的CSS 的话，这又需要很多计算。<br>这就可以用到throttle()函数:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">resizeDiv</span>(<span class="params"></span>)</span>&#123;</div><div class="line"><span class="keyword">var</span> div = <span class="built_in">document</span>.getElementById(<span class="string">"myDiv"</span>);</div><div class="line">div.style.height = div.offsetWidth + <span class="string">"px"</span>;</div><div class="line">&#125;</div><div class="line"><span class="built_in">window</span>.onresize = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</div><div class="line">throttle(resizeDiv);</div><div class="line">&#125;;</div></pre></td></tr></table></figure>
<p>调整大小的功能被放入了一个叫做resizeDiv()的单独函数中。<br>然后onresize 事件处理程序调用throttle()并传入resizeDiv 函数，而不是直接调用resizeDiv()。<br>多数情况下，用户是感觉不到变化的，虽然给浏览器节省的计算可能会非常大。</p>
<p>只要代码是周期性执行的，都应该使用节流，但是你不能控制请求执行的速率。</p>

      
    </div>

    <div>
      
        

      
    </div>

    <div>
      
        

      
    </div>


    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/Web/" rel="tag"># Web</a>
          
            <a href="/tags/JavaScript/" rel="tag"># JavaScript</a>
          
        </div>
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2016/11/07/javascript-note-22-1/" rel="next" title="《 JavaScript 高级程序设计》备忘-第二十二章-1">
                <i class="fa fa-chevron-left"></i> 《 JavaScript 高级程序设计》备忘-第二十二章-1
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2016/11/08/javascript-note-22-3/" rel="prev" title="《 JavaScript 高级程序设计》备忘-第二十二章-3">
                《 JavaScript 高级程序设计》备忘-第二十二章-3 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          
  <div class="comments" id="comments">
    
  </div>


        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap" >
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
          <img class="site-author-image" itemprop="image"
               src="/uploads/avatar.jpg"
               alt="Huang SJ" />
          <p class="site-author-name" itemprop="name">Huang SJ</p>
          <p class="site-description motion-element" itemprop="description"></p>
        </div>
        <nav class="site-state motion-element">
          <div class="site-state-item site-state-posts">
            <a href="/archives">
              <span class="site-state-item-count">75</span>
              <span class="site-state-item-name">日志</span>
            </a>
          </div>

          
            <div class="site-state-item site-state-categories">
              <a href="/categories">
                <span class="site-state-item-count">4</span>
                <span class="site-state-item-name">分类</span>
              </a>
            </div>
          

          
            <div class="site-state-item site-state-tags">
              <a href="/tags">
                <span class="site-state-item-count">9</span>
                <span class="site-state-item-name">标签</span>
              </a>
            </div>
          

        </nav>

        

        <div class="links-of-author motion-element">
          
            
              <span class="links-of-author-item">
                <a href="https://github.com/hsj1992" target="_blank" title="GitHub">
                  
                    <i class="fa fa-fw fa-github"></i>
                  
                  GitHub
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="https://twitter.com/hsj9527" target="_blank" title="Twitter">
                  
                    <i class="fa fa-fw fa-twitter"></i>
                  
                  Twitter
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="http://weibo.com/u/1694147905" target="_blank" title="Weibo">
                  
                    <i class="fa fa-fw fa-weibo"></i>
                  
                  Weibo
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="https://www.freecodecamp.cn/hsj1992" target="_blank" title="FreeCodeCamp">
                  
                    <i class="fa fa-fw fa-fire"></i>
                  
                  FreeCodeCamp
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="http://codepen.io/hsj1992/" target="_blank" title="codepen">
                  
                    <i class="fa fa-fw fa-codepen"></i>
                  
                  codepen
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="http://hsj1992.oschina.io/" target="_blank" title="码云镜像">
                  
                    <i class="fa fa-fw fa-code"></i>
                  
                  码云镜像
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="https://hsj1992.coding.me/" target="_blank" title="coding镜像">
                  
                    <i class="fa fa-fw fa-code"></i>
                  
                  coding镜像
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="https://hsj1992.github.io/" target="_blank" title="github镜像">
                  
                    <i class="fa fa-fw fa-code"></i>
                  
                  github镜像
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="https://codingme-6dd8a.firebaseapp.com" target="_blank" title="Firebase镜像">
                  
                    <i class="fa fa-fw fa-google"></i>
                  
                  Firebase镜像
                </a>
              </span>
            
          
        </div>

        
        

        
        

        


      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#高级技巧"><span class="nav-number">1.</span> <span class="nav-text">高级技巧</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#高级函数"><span class="nav-number">1.1.</span> <span class="nav-text">高级函数</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#函数柯里化"><span class="nav-number">1.1.1.</span> <span class="nav-text">函数柯里化</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#防篡改对象"><span class="nav-number">1.2.</span> <span class="nav-text">防篡改对象</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#不可扩展对象"><span class="nav-number">1.2.1.</span> <span class="nav-text">不可扩展对象</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#密封的对象"><span class="nav-number">1.2.2.</span> <span class="nav-text">密封的对象</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#冻结的对象"><span class="nav-number">1.2.3.</span> <span class="nav-text">冻结的对象</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#高级定时器"><span class="nav-number">1.3.</span> <span class="nav-text">高级定时器</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#重复的定时器"><span class="nav-number">1.3.1.</span> <span class="nav-text">重复的定时器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Yielding-Processes"><span class="nav-number">1.3.2.</span> <span class="nav-text">Yielding Processes</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#函数节流"><span class="nav-number">1.3.3.</span> <span class="nav-text">函数节流</span></a></li></ol></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright" >
  
  &copy;  2015 - 
  <span itemprop="copyrightYear">2016</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Huang SJ</span>
</div>


<div class="powered-by">
  由 <a class="theme-link" href="https://hexo.io">Hexo</a> 强力驱动
</div>

<div class="theme-info">
  主题 -
  <a class="theme-link" href="https://github.com/iissnan/hexo-theme-next">
    NexT.Pisces
  </a>
</div>


        

        
      </div>
    </footer>

    <div class="back-to-top">
      <i class="fa fa-arrow-up"></i>
    </div>
  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  



  
  <script type="text/javascript" src="//cdn.staticfile.org/jquery/2.1.3/jquery.min.js"></script>

  
  <script type="text/javascript" src="//cdn.staticfile.org/fastclick/1.0.6/fastclick.min.js"></script>

  
  <script type="text/javascript" src="//cdn.staticfile.org/jquery_lazyload/1.9.7/jquery.lazyload.min.js"></script>

  
  <script type="text/javascript" src="//cdn.staticfile.org/velocity/1.2.1/velocity.min.js"></script>

  
  <script type="text/javascript" src="//cdn.staticfile.org/velocity/1.2.1/velocity.ui.min.js"></script>

  
  <script type="text/javascript" src="//cdn.staticfile.org/fancybox/2.1.5/jquery.fancybox.pack.js"></script>


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.0"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.0"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=5.1.0"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.0"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.0"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.0"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.0"></script>



  



  




	




  
  
  <script type="text/javascript">
    // Popup Window;
    var isfetched = false;
    // Search DB path;
    var search_path = "search.xml";
    if (search_path.length == 0) {
      search_path = "search.xml";
    }
    var path = "/" + search_path;
    // monitor main search box;

    function proceedsearch() {
      $("body").append('<div class="popoverlay">').css('overflow', 'hidden');
      $('.popup').toggle();
    }
    // search function;
    var searchFunc = function(path, search_id, content_id) {
      'use strict';
      $.ajax({
        url: path,
        dataType: "xml",
        async: true,
        success: function( xmlResponse ) {
          // get the contents from search data
          isfetched = true;
          $('.popup').detach().appendTo('.header-inner');
          var datas = $( "entry", xmlResponse ).map(function() {
            return {
              title: $( "title", this ).text(),
              content: $("content",this).text(),
              url: $( "url" , this).text()
            };
          }).get();
          var $input = document.getElementById(search_id);
          var $resultContent = document.getElementById(content_id);
          $input.addEventListener('input', function(){
            var matchcounts = 0;
            var str='<ul class=\"search-result-list\">';
            var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
            $resultContent.innerHTML = "";
            if (this.value.trim().length > 1) {
              // perform local searching
              datas.forEach(function(data) {
                var isMatch = false;
                var content_index = [];
                var data_title = data.title.trim().toLowerCase();
                var data_content = data.content.trim().replace(/<[^>]+>/g,"").toLowerCase();
                var data_url = decodeURIComponent(data.url);
                var index_title = -1;
                var index_content = -1;
                var first_occur = -1;
                // only match artiles with not empty titles and contents
                if(data_title != '') {
                  keywords.forEach(function(keyword, i) {
                    index_title = data_title.indexOf(keyword);
                    index_content = data_content.indexOf(keyword);
                    if( index_title >= 0 || index_content >= 0 ){
                      isMatch = true;
                      if (i == 0) {
                        first_occur = index_content;
                      }
                    }

                  });
                }
                // show search results
                if (isMatch) {
                  matchcounts += 1;
                  str += "<li><a href='"+ data_url +"' class='search-result-title'>"+ data_title +"</a>";
                  var content = data.content.trim().replace(/<[^>]+>/g,"");
                  if (first_occur >= 0) {
                    // cut out 100 characters
                    var start = first_occur - 20;
                    var end = first_occur + 80;
                    if(start < 0){
                      start = 0;
                    }
                    if(start == 0){
                      end = 50;
                    }
                    if(end > content.length){
                      end = content.length;
                    }
                    var match_content = content.substring(start, end);
                    // highlight all keywords
                    keywords.forEach(function(keyword){
                      var regS = new RegExp(keyword, "gi");
                      match_content = match_content.replace(regS, "<b class=\"search-keyword\">"+keyword+"</b>");
                    });

                    str += "<p class=\"search-result\">" + match_content +"...</p>"
                  }
                  str += "</li>";
                }
              })};
            str += "</ul>";
            if (matchcounts == 0) { str = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>' }
            if (keywords == "") { str = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>' }
            $resultContent.innerHTML = str;
          });
          proceedsearch();
        }
      });}

    // handle and trigger popup window;
    $('.popup-trigger').click(function(e) {
      e.stopPropagation();
      if (isfetched == false) {
        searchFunc(path, 'local-search-input', 'local-search-result');
      } else {
        proceedsearch();
      };
    });

    $('.popup-btn-close').click(function(e){
      $('.popup').hide();
      $(".popoverlay").remove();
      $('body').css('overflow', '');
    });
    $('.popup').click(function(e){
      e.stopPropagation();
    });
  </script>


  

  

  

  


</body>
</html>
